/*
 * Copyright (c) 2010 CCX(China) Co.,Ltd. All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * CCX(China) Co.,Ltd. ("Confidential Information").
 * It may not be copied or reproduced in any manner without the express 
 * written permission of CCX(China) Co.,Ltd.
 * 
 *	Created on 2010-12-13 下午05:01:14
 */
package cn.com.ccxe.core.db;


import java.util.ArrayList;
import java.util.Comparator;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.CursorConfig;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.Transaction;

public abstract class BasicDB<E> {

	protected Database database;
	protected Lock lock = new ReentrantLock();
	final Logger logger = LoggerFactory.getLogger(BasicDB.class);

	/**
	 * 使用自定义配置来构建Database
	 * 
	 * @param env
	 * @param name
	 * @param dbConfig
	 * @return
	 */
	protected BasicDB(Environment env, String name, DatabaseConfig dbConfig) {
		database = env.openDatabase(null, name, dbConfig);
	}

	protected BasicDB() {}

	/**
	 * 添加数据<br>
	 * 如果数据库中存在该条数据，将不覆盖。
	 * @param key
	 * @param e
	 */
	public void put(String key, E e) {
		isEmpty();
		DatabaseEntry valueEntry;
		DatabaseEntry keyEntry;
		try {
			lock.lock();
			valueEntry = convert(e);
			keyEntry = new DatabaseEntry(key.getBytes());
			database.putNoOverwrite(null, keyEntry, valueEntry);
		} catch (DatabaseException ex) {
			throw ex;
		} finally {
			lock.unlock();
		}
	}

	/**
	 * 根据url从WebDB中获取最先出现的数据。
	 * 
	 * @param key
	 * @return
	 */
	public E getFirst(String key) {

		DatabaseEntry keyEntry;
		DatabaseEntry dataEntry = new DatabaseEntry();
		OperationStatus result = null;

		try {
			lock.lock();
			isEmpty();
			keyEntry = new DatabaseEntry(key.getBytes());
			result = database.get(null, keyEntry, dataEntry, null);
			return isSuccess(dataEntry, result) ? convert(dataEntry) : null;
		} finally {
			lock.unlock();
		}
	}

	/**
	 * 更新
	 * 
	 * @param key
	 * @param E
	 */
	public void update(String key, E e) {
		DatabaseEntry dataEntry;
		DatabaseEntry keyEntry;
		try {
			lock.lock();
			isEmpty();
			keyEntry = new DatabaseEntry(key.getBytes());
			dataEntry = convert(e);
			if (isEmpty(keyEntry, dataEntry)) {
				database.put(null, keyEntry, dataEntry);
			}
		} finally {
			lock.unlock();
		}
	}

	public void delete(String key) {
		DatabaseEntry keyEntry = new DatabaseEntry(key.getBytes());
		lock.lock();
		try {
			database.delete(null, keyEntry);
		} finally {
			lock.unlock();
		}
	}

	/**
	 * 获取档案总数
	 * 
	 * @return
	 */
	public int size() {
		try {
			isEmpty();
			return (int) database.count();
		} catch (DatabaseException e) {
			e.printStackTrace();
		}
		return -1;
	}

	/**
	 * 消耗工作队列中的资源
	 */
	public ArrayList<E> consume(int max) {
		try {
			lock.lock();
			ArrayList<E> list = get(max);
			delete(list.size());
			return list;
		} finally {
			lock.unlock();
		}
	}

	public Cursor getCursor(Transaction txn,CursorConfig cursorConfig) {
		return database.openCursor(null, cursorConfig);
	}
	public Cursor getCursor() {
		return database.openCursor(null, null);
	}
	
	public String getDatabaseName() {
		return database.getDatabaseName();
	}

	/**
	 * 物理同步
	 */
	public void sync() {
		if (database == null) {
			return;
		}
		try {
			lock.lock();
			database.sync();
		} catch (DatabaseException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public void close() {
		try {
			lock.lock();
			if(database == null) {
				database.close();
				database = null;
			}
		} finally {
			lock.unlock();
		}
	}

	/**
	 * 获取数据库
	 * 
	 * @return
	 */
	protected Database getDatabase() {
		return database;
	}

	/**
	 * 判断查询数据 库操作是否成功与返回值是否为空
	 * 
	 * @param data
	 * @param result
	 * @return
	 */
	protected boolean isSuccess(DatabaseEntry data, OperationStatus result) {
		return result == OperationStatus.SUCCESS && data.getData().length > 0;
	}

	/**
	 * 删除工作队列中的资源 PS:非线程安全，在进行批量操作时并为锁定数据库，调用此方法时，必须加上锁定操作.
	 * 
	 * @param count
	 * @throws DatabaseException
	 */
	protected void delete(int count) throws DatabaseException {
		DatabaseEntry key = new DatabaseEntry();
		DatabaseEntry value = new DatabaseEntry();
		Cursor cursor = database.openCursor(null, null);
		OperationStatus result = null;
		int matches = 0;

		try {
			result = cursor.getFirst(key, value, null);
			while (matches < count && isSuccess(value, result)) {
				cursor.delete();
				matches++;
				result = cursor.getNext(key, value, null);
			}
		} finally {
			closeCursor(cursor);
		}
	}


	/**
	 * 批量获取队列中的数据 ps:非线程安全，在进行批量操作时并为锁定数据库，调用此方法时，必须加上锁定操作.
	 * 
	 * @param max
	 * @return WebURL集合
	 * @throws DatabaseException
	 */
	protected E get() throws DatabaseException {
		
		DatabaseEntry keyEntry = new DatabaseEntry();
		DatabaseEntry valueEntry = new DatabaseEntry();
		Cursor cursor = database.openCursor(null, null);
		OperationStatus result = null;
		try {
			result = cursor.getFirst(keyEntry, valueEntry, null);
			if(isSuccess(valueEntry, result)) {
				E e = convert(valueEntry);
				return e;
			}
		} finally {
			closeCursor(cursor);
		}
		return null;
	}
	
	/**
	 * 批量获取队列中的数据 ps:非线程安全，在进行批量操作时并为锁定数据库，调用此方法时，必须加上锁定操作.
	 * 
	 * @param max
	 * @return WebURL集合
	 * @throws DatabaseException
	 */
	protected ArrayList<E> get(int max) throws DatabaseException {

		ArrayList<E> results = new ArrayList<E>(max);
		DatabaseEntry keyEntry = new DatabaseEntry();
		DatabaseEntry valueEntry = new DatabaseEntry();
		Cursor cursor = database.openCursor(null, null);
		OperationStatus result = null;
		int matches = 0;

		try {
			result = cursor.getFirst(keyEntry, valueEntry, null);
			while (matches < max && isSuccess(valueEntry, result)) {
				E e = convert(valueEntry);
				results.add(e);
				matches++;
				result = cursor.getNext(keyEntry, valueEntry, null);
			}
		} finally {
			closeCursor(cursor);
		}
		return results;
	}
	
	/**
	 * 获取游标当前指定的数据 PS:此类并不对游标进行关闭等操作，在使用此方法时，还需对游标进行管理
	 * 
	 * @return
	 */
	protected E getNext(Cursor cursor) {
		DatabaseEntry dataEntry = new DatabaseEntry();
		DatabaseEntry keyEntry = new DatabaseEntry();

		OperationStatus result = cursor.getNext(keyEntry, dataEntry, null);
		return isSuccess(dataEntry, result) ? convert(dataEntry) : null;
	}

	/**
	 * 将数据库元数据转换成WebURL
	 * 
	 * @param data
	 * @return
	 */
	protected E convert(DatabaseEntry data) {
		TupleBinding<E> binding = getTupleBinding();
		if(binding == null) {
			binding = getTupleBinding();
			logger.info("setTupleBinding!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
		}
		return binding.entryToObject(data);
	}

	protected void closeCursor(Cursor cursor) throws DatabaseException {
		if (cursor != null) {
			cursor.close();
			cursor = null;
		}
	}

	protected abstract TupleBinding<E> getTupleBinding();

	protected abstract Class<? extends Comparator<byte[]>> getComparator();

	/**
	 * 判断数据库是否为空
	 * 
	 * @return
	 */
	private boolean isEmpty() {
		boolean flag = true;
		if (database == null) {
			flag = false;
			throw new NullPointerException("数据库未创建！");
		}
		return flag;
	}

	/**
	 * 将WebURL装换成数据库元数据
	 * 
	 * @param curURL
	 * @return
	 */
	private DatabaseEntry convert(E e) {
		DatabaseEntry data = new DatabaseEntry();
		getTupleBinding().objectToEntry(e, data);
		return data;
	}
	
	/**
	 * 判断数据是否为空
	 * 
	 * @param key
	 * @param data
	 * @return
	 */
	private boolean isEmpty(DatabaseEntry key, DatabaseEntry data) {
		return key != null && data != null;
	}
	
}
